/*****************************************************************************
 * Copyright (C) Stacy Curl. All rights reserved.
 *
 * The software in this package is published under the terms of the BSD
 * style license a copy of which has been included with this distribution in
 * the LICENSE.txt file.
 *****************************************************************************/
package picounit.features.base;

import picounit.PicoUnitException;

import java.util.List;
import java.util.Map;

class DefaultFeatureResolver implements FeatureResolver {
	private final List<Object> extensions;
	private final Map<Class<?>, AggregateExtension<?>> extensionsMap;
	
	public DefaultFeatureResolver(List<Object> extensions, Map<Class<?>, AggregateExtension<?>> extensionsMap) {
		this.extensions = extensions;
		this.extensionsMap = extensionsMap;
	}

	@SuppressWarnings("unchecked")
	public <T> T getFeature(Class<T> extensionType) {
		if (AggregateExtension.class.isAssignableFrom(extensionType)) {
			AggregateExtension<T> aggregateExtension =
				instantiate((Class<AggregateExtension<T>>) extensionType);

			return getFeature(aggregateExtension.extensionType(), aggregateExtension);
		}

		return getFeature(extensionType, new DynamicProxyAggregateExtension<T>(extensionType));
	}
	
	///////////////////////////////////////////////////////////////////////////////////////////////
	// Implementation methods
	///////////////////////////////////////////////////////////////////////////////////////////////

	@SuppressWarnings("unchecked")
	private <T> T getFeature(Class<T> extensionType, AggregateExtension<T> aggregate) {
		if (extensionsMap.containsKey(extensionType)) {
			return (T) extensionsMap.get(extensionType).get();
		}
	
		for (Object extension : extensions) {
			aggregate.addIfMatches(extension);
		}
	
		extensionsMap.put(extensionType, aggregate);
	
		return aggregate.get();
	}

	private <T, AE extends AggregateExtension<T>> AE instantiate(Class<AE> aggregateExtensionType) {
		try {
			return aggregateExtensionType.newInstance();
		}
		catch (InstantiationException instantiationException) {
			throw new PicoUnitException();
		}
		catch (IllegalAccessException illegalAccessException) {
			throw new PicoUnitException();
		}
	}
}